package com.ljb.lib_ar;

import android.opengl.GLES20;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import cn.easyar.Buffer;
import cn.easyar.CameraDevice;
import cn.easyar.CameraDeviceFocusMode;
import cn.easyar.CameraDevicePreference;
import cn.easyar.CameraDeviceSelector;
import cn.easyar.CameraDeviceType;
import cn.easyar.CameraParameters;
import cn.easyar.DelayedCallbackScheduler;
import cn.easyar.FeedbackFrameFork;
import cn.easyar.FrameFilterResult;
import cn.easyar.FunctorOfVoidFromTargetAndBool;
import cn.easyar.Image;
import cn.easyar.ImageTarget;
import cn.easyar.ImageTracker;
import cn.easyar.ImageTrackerResult;
import cn.easyar.InputFrame;
import cn.easyar.InputFrameFork;
import cn.easyar.InputFrameThrottler;
import cn.easyar.InputFrameToFeedbackFrameAdapter;
import cn.easyar.InputFrameToOutputFrameAdapter;
import cn.easyar.Matrix44F;
import cn.easyar.OutputFrame;
import cn.easyar.OutputFrameBuffer;
import cn.easyar.OutputFrameFork;
import cn.easyar.OutputFrameJoin;
import cn.easyar.StorageType;
import cn.easyar.Target;
import cn.easyar.TargetInstance;
import cn.easyar.TargetStatus;
import cn.easyar.Vec2I;

/**
 * @author ljb
 * @des
 * @date 2019/11/16
 */
public class SimpleAR {
    private static final String TAG = "SimpleAR";
    private DelayedCallbackScheduler scheduler;
    private ArrayList<ImageTracker> trackers;

    private CameraDevice camera;
    private InputFrameThrottler throttler;
    private InputFrameFork inputFrameFork;
    private OutputFrameJoin join;
    private OutputFrameBuffer oFrameBuffer;
    private InputFrameToOutputFrameAdapter i2OAdapter;
    private InputFrameToFeedbackFrameAdapter i2FAdapter;
    private OutputFrameFork outputFrameFork;
    private FeedbackFrameFork feedbackFrameFork;

    private BGRenderer bgRenderer;
    private int previousInputFrameIndex = -1;
    private int active_target = 0;
    private long lastTime;

    private OnTargetChangeListener onTargetChangeListener;
    private String path;

    public void setTargetListener(String path, OnTargetChangeListener onTargetChangeListener) {
        this.path = path;
        this.onTargetChangeListener = onTargetChangeListener;
    }

    public SimpleAR() {
        scheduler = new DelayedCallbackScheduler();
        trackers = new ArrayList<>();
    }

    /**
     * 初始化
     */
    public void initialize() {
        recreateContext();

        //创建相机设备
        camera = CameraDeviceSelector.createCameraDevice(CameraDevicePreference.PreferObjectSensing);
        //输入帧节流器
        throttler = InputFrameThrottler.create();
        //输入帧分流器
        inputFrameFork = InputFrameFork.create(2);
        //输出帧合流器
        join = OutputFrameJoin.create(2);
        //输出帧缓存
        oFrameBuffer = OutputFrameBuffer.create();
        //输入帧到输出帧适配器
        i2OAdapter = InputFrameToOutputFrameAdapter.create();
        //输入帧到反馈帧适配器
        i2FAdapter = InputFrameToFeedbackFrameAdapter.create();
        //输出帧分流器
        outputFrameFork = OutputFrameFork.create(2);

        //打开摄像头
        if (camera.openWithType(CameraDeviceType.Default)) {
            camera.setSize(new Vec2I(1280, 720));
            //设置相机自动聚焦
            camera.setFocusMode(CameraDeviceFocusMode.Continousauto);
        }
        // 实现平面卡片的检测和跟踪
        ImageTracker tracker = ImageTracker.create();
        if (!TextUtils.isEmpty(path)) {
            loadFromJson(tracker, path);
        }
        trackers.add(tracker);

        //反馈帧分流器
        feedbackFrameFork = FeedbackFrameFork.create(trackers.size());
        //连接输入输入端口
        camera.inputFrameSource().connect(throttler.input());
        throttler.output().connect(inputFrameFork.input());
        inputFrameFork.output(0).connect(i2OAdapter.input());
        i2OAdapter.output().connect(join.input(0));
        inputFrameFork.output(1).connect(i2FAdapter.input());
        i2FAdapter.output().connect(feedbackFrameFork.input());

        for (int i = 0; i < trackers.size(); i++) {
            ImageTracker imageTracker = trackers.get(i);
            feedbackFrameFork.output(i).connect(imageTracker.feedbackFrameSink());
            imageTracker.outputFrameSource().connect(join.input(i + 1));
        }

        join.output().connect(outputFrameFork.input());
        outputFrameFork.output(0).connect(oFrameBuffer.input());
        outputFrameFork.output(1).connect(i2FAdapter.sideInput());
        oFrameBuffer.signalOutput().connect(throttler.signalInput());
    }

    /**
     * 开始
     */
    public boolean start() {
        if (camera == null || !camera.start()) {
            return false;
        }
        for (ImageTracker tracker : trackers) {
            if (!tracker.start()) {
                return false;
            }
        }
        return true;
    }

    /**
     * 停止
     */
    public void stop() {
        if (camera != null) {
            camera.stop();
        }
        for (ImageTracker tracker : trackers) {
            tracker.stop();
        }
    }

    /**
     * 释放资源
     */
    public void dispose() {
        for (ImageTracker tracker : trackers) {
            tracker.dispose();
        }
        trackers.clear();
        if (bgRenderer != null) {
            bgRenderer.dispose();
            bgRenderer = null;
        }
        if (camera != null) {
            camera.dispose();
            camera = null;
        }
        if (scheduler != null) {
            scheduler.dispose();
            scheduler = null;
        }
    }

    /**
     * 初始化资源
     */
    public void recreateContext() {
        if (bgRenderer != null) {
            bgRenderer.dispose();
            bgRenderer = null;
        }
        previousInputFrameIndex = -1;
        bgRenderer = new BGRenderer();
    }

    public void render(int width, int height, int screenRotation) {
        //执行一个回调。如果没有回调可执行，则返回false
        while (scheduler.runOne()) {
        }

        GLES20.glViewport(0, 0, width, height);
        GLES20.glClearColor(0.f, 0.f, 0.f, 1.f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        OutputFrame oframe = oFrameBuffer.peek();
        if (oframe == null) {
            return;
        }
        InputFrame iframe = oframe.inputFrame();
        if (iframe == null) {
            oframe.dispose();
            return;
        }
        CameraParameters cameraParameters = iframe.cameraParameters();
        if (cameraParameters == null) {
            oframe.dispose();
            iframe.dispose();
            return;
        }
        float viewportAspectRatio = (float) width / (float) height;
        Matrix44F imageProjection = cameraParameters.imageProjection(viewportAspectRatio, screenRotation, true, false);
        Image image = iframe.image();

        try {
            if (iframe.index() != previousInputFrameIndex) {
                Buffer buffer = image.buffer();
                try {
                    byte[] bytes = new byte[buffer.size()];
                    buffer.copyToByteArray(bytes);
                    bgRenderer.upload(image.format(), image.width(), image.height(), ByteBuffer.wrap(bytes));
                } finally {
                    buffer.dispose();
                }
                previousInputFrameIndex = iframe.index();
            }
            bgRenderer.render(imageProjection);

            for (FrameFilterResult oResult : oframe.results()) {
                ImageTrackerResult result = (ImageTrackerResult) oResult;
                if (result != null) {
                    for (TargetInstance targetInstance : result.targetInstances()) {
                        int status = targetInstance.status();
                        if (status == TargetStatus.Tracked) {
                            Target target = targetInstance.target();
                            if (target == null) {
                                continue;
                            }
                            Log.i(TAG, "render: " + target.name() + "," + target.runtimeID());

                            //去除重复触发
                            int id = target.runtimeID();
//                            Log.e(TAG, "render: active_target="+active_target );
                            if (active_target != id) {
                                active_target = id;
//                                Log.e(TAG, "render: id=" + id);
                                //回调
                                if (onTargetChangeListener != null) {
                                    onTargetChangeListener.onTargetChange(target);
                                }
                            } else {
                                if (System.currentTimeMillis() - lastTime > 500) {
                                    active_target = 0;
//                                    Log.e(TAG, "render: active_target初始化" );
                                }
                            }
                            lastTime = System.currentTimeMillis();
                        }
                    }
                    result.dispose();
                }
            }
        } finally {
            iframe.dispose();
            oframe.dispose();
            cameraParameters.dispose();
            image.dispose();
        }
    }

    /**
     * 批量加载 path可以是json字符串,也可以是json文件的路径,json文件中的路径,应为绝对路径或或相对于json文件的相对路径。
     *
     * @param tracker
     * @param path
     */
    private void loadFromJson(final ImageTracker tracker, final String path) {
        ArrayList<ImageTarget> imageTargets = ImageTarget.setupAll(path, StorageType.App);
        for (ImageTarget target : imageTargets) {
            if (target == null) {
                Log.e(TAG, "target create failed or key is not correct");
                continue;
            }

            tracker.loadTarget(target, scheduler, new FunctorOfVoidFromTargetAndBool() {
                @Override
                public void invoke(Target target, boolean status) {
                    Log.i(TAG, String.format("load target (%b): %s (%d)", status, target.name(), target.runtimeID()));
                }
            });
        }
    }

    //++++++++++++++++++++++++++++++++++++++++    ++++++++++++++++++++++++++++++++++++++++//


    /**
     * 将图片转换为etd文件
     *
     * @param tracker
     * @param path
     */
    private void loadFromJson2(final ImageTracker tracker, final String path) {
        ArrayList<ImageTarget> imageTargets = ImageTarget.setupAll(path, StorageType.App);
        totalImageCount = imageTargets.size();

        for (ImageTarget target : imageTargets) {
            if (target == null) {
                Log.e(TAG, "target create failed or key is not correct");
                continue;
            }

            tracker.loadTarget(target, scheduler, new FunctorOfVoidFromTargetAndBool() {
                @Override
                public void invoke(Target target, boolean status) {
                    Log.i(TAG, String.format("load target (%b): %s (%d)", status, target.name(), target.runtimeID()));
                    completedCount++;
                }
            });
        }

        new Thread() {
            @Override
            public void run() {
                save(tracker, path);
            }
        }.start();
    }

    /**
     * 总图片数
     */
    private int totalImageCount = 0;
    /**
     * 已完成数
     */
    private int completedCount = 0;


    private void save(ImageTracker tracker, String path) {
        while (completedCount < totalImageCount) {
            //图片加载完成
            SystemClock.sleep(500);
            Log.e(TAG, "save: wait..." + totalImageCount + "," + completedCount);
        }
        Log.e(TAG, "save: 开始转换============>");
        Log.e(TAG, "save: 图片总数=" + totalImageCount + ",加载总数为=" + completedCount);

        ArrayList<Target> targets = tracker.targets();
        for (Target target : targets) {
            if (target instanceof ImageTarget) {
                ImageTarget imageTarget = (ImageTarget) target;
                File file = new File(path);
                String etds = file.getParent() + "/etds";
                File file1 = new File(etds);
                if (!file1.exists()) {
                    file1.mkdirs();
                }

                boolean save = imageTarget.save(etds);
                Log.e(TAG, "save: path = " + etds + ",ret= " + save);
            }
        }
        Log.e(TAG, "save: 转换完成==========>");
    }
}
